const {ccclass, property} = cc._decorator;

export var listDir=cc.Enum({
    VERTICAL:0,
    HORIZONTAL:1
});
@ccclass
export default class TableView extends cc.Component {
    @property(cc.ScrollView)
    scrollView:cc.ScrollView=null;
    @property(cc.Node)
    topTip:cc.Node=null;
    @property(cc.Node)
    bottomTip:cc.Node=null;
    @property
    space:number=0;
    @property({type:listDir})
    direction=listDir.VERTICAL;

    _count=0;
    _cellPositions:any[];
    _lastUpdateOffset:cc.Vec2;
    _content:cc.Node;
    _willRemovedCells:any;
    _cells:any;
    _handler:any;
    _viewSize:cc.Size;
    _startIndex:number;
    _endIndex:number;
    _cellOffset:cc.Vec2=cc.v2(0,0);
    _firstCellSize:cc.Size;

    setScrollView(view){
        this.scrollView=view;
        this._content=this.scrollView.content;
        this.setDirection(this.direction);
    }
    setCellOffset(offset){
        this._cellOffset=offset;
    }
    setDirection(direction){
        this.direction=direction;
        if(this.direction==listDir.VERTICAL){
            this.scrollView.content.setAnchorPoint(0.5,1);
        }
        else{
            this.scrollView.content.setAnchorPoint(0,0.5);
        }
    }
    update(dt){
        for(var i in this._willRemovedCells){
            var cell=this._cells[i];
            cell.removeFromParent();
            this._handler("removeCell", this, cell);
            delete this._cells[i];
        }
        this._willRemovedCells={};

        var isUpdate=true,curOffset = this._content.getPosition();
        if(this._lastUpdateOffset&&cc.pDistance(this._lastUpdateOffset,curOffset)<=2){
            isUpdate=false;
        }
        if(isUpdate){
            this._lastUpdateOffset=curOffset;
            this.updateCells();
        }
    }
    setHandler(handler){
        this._handler=handler;
    }
    reloadData(isNotResetOffset?:any){
        for(var i in this._cells){
            var cell = this._cells[i];
            cell.removeFromParent();
            this._handler("removeCell", this, cell);
        }
        this._viewSize=this.scrollView.node.getContentSize();
        this._cells={};
        this._willRemovedCells={};
        this._startIndex=-1;
        this._count=this._handler('count',this);
        if(this._count<=0){
            return;
        }
        this.updateCellPosition();
        this.updateContentSize(isNotResetOffset);
        this._lastUpdateOffset=null;
    }
    updateCellPosition(){
        this._cellPositions=[];
        for(var i=0;i<=this._count+1;i++){
            this._cellPositions.push(0);
        }
        var currentPos=0;
        if(this.direction==listDir.VERTICAL){
            currentPos=this._cellOffset.y;
        }
        else{
            currentPos=this._cellOffset.x;
        }
        var cellSize;
        for(var i=0;i<this._count;++i){
            this._cellPositions[i]=currentPos;
            cellSize=this._handler('cellSize',this,i);
            if(!this._firstCellSize){
                this._firstCellSize=cellSize;
            }
            if(this.direction==listDir.VERTICAL){
                currentPos+=cellSize.height;
            }
            else{
                currentPos+=cellSize.width;
            }
            currentPos+=this.space;
        }
        this._cellPositions[this._count]=currentPos;
    }
    updateContentSize(isNotResetOffset){
        var size,maxPosition=this._cellPositions[this._count]-this.space;
        if(this.direction==listDir.VERTICAL){
            size=cc.size(this._viewSize.width,Math.max(maxPosition,this._viewSize.height));
            if(size.width<this._firstCellSize.width){
                size.width=this._firstCellSize.width;
            }
        }
        else{
            size=cc.size(Math.max(maxPosition,this._viewSize.width),this._viewSize.height);
            if(size.height<this._firstCellSize.height){
                size.height=this._firstCellSize.height;
            }
        }
        this.scrollView.content.setContentSize(size);
        if(!isNotResetOffset){
            if(this.direction==listDir.VERTICAL){
                this.scrollView.content.setPosition(0,this._viewSize.height*0.5);
            }
            else{
                this.scrollView.content.setPosition(-this._viewSize.width*0.5,0);
            }
        }
    }
    indexFromOffset(offset:cc.Vec2){
        offset=cc.p(offset.x,offset.y);
        if(this.direction==listDir.VERTICAL){
            offset.y=this._content.getContentSize().height-offset.y;
        }
        var low=0, high=this._count-1,search;
        if(this.direction==listDir.VERTICAL){
            search=offset.y;
        }
        else{
            search=offset.x;
        }
        if(search<=this._cellPositions[low]){
            return low;
        }
        else if(search>=this._cellPositions[high]){
            return high;
        }
        while (high>=low) {
            var index=Math.floor(low+(high-low)*0.5);
            var cellStart=this._cellPositions[index];
            var cellEnd=this._cellPositions[index+1];
            if(search>=cellStart&&search<=cellEnd){
                return index;
            }
            else if(search<cellStart){
                high=index-1;
            }
            else{
                low=index+1;
            }
        }
    }
    offsetFromIndex(index:number){
        var offset;
        if(this.direction==listDir.VERTICAL){
            offset=cc.p(0,this._cellPositions[index]);
            offset.y=this._content.height-offset.y-(this._cellPositions[index+1]-this._cellPositions[index]-this.space);
        }
        else{
            offset=cc.p(this._cellPositions[index],0);
        }
        return offset;
    }
    updateCells(){
        if(this._count<=0){
            return;
        }
        var startOffset=null;
        if(this.direction==listDir.VERTICAL){
            startOffset=cc.p(0,this._content.getContentSize().height-this._content.getPositionY()+ this._viewSize.height * 0.5);
        }
        else {
            startOffset = cc.p(-this._content.getPositionX() - this._viewSize.width * 0.5, 0)
        }
        var endOffset = cc.p(startOffset.x, startOffset.y);

        var startIndex,endIndex;
        if (this.direction == listDir.VERTICAL) {
            endOffset.y = startOffset.y - this._viewSize.height;
            startIndex = this.indexFromOffset(startOffset);
            endIndex = this.indexFromOffset(endOffset);
        } 
        else {
            endOffset.x += this._viewSize.width;
            startIndex = this.indexFromOffset(startOffset);
            endIndex = this.indexFromOffset(endOffset);
        }
        if (this._startIndex >= 0) {
            for (var i = this._startIndex; i < startIndex && i <= this._endIndex; ++i) {
                if (this._cells[i]) {
                    this._willRemovedCells[i] = true;
                }
            }
        }
        if (this._endIndex >= 0) {
            for (var i: number = endIndex; i <= this._endIndex && i >= this._startIndex; ++i) {
                if (this._cells[i]) {
                    this._willRemovedCells[i] = true;
                }
            }
        }
        this._startIndex = startIndex;
        this._endIndex = endIndex;
        for (var i: number = startIndex; i <= endIndex; ++i) {
            delete this._willRemovedCells[i];
            if (!this._cells[i]) {
                this.updateCellAtIndex(i);
            }
        }
    }
    updateCellAtIndex(index){
        if (index < 0 || index >= this._count) {
            return;
        }
        delete this._willRemovedCells[index];
        let _cell = this._cells[index];
        if (_cell) {
            _cell.removeFromParent();
            this._handler("removeCell", this, _cell);
        }
        let cell = this._handler("cell", this, index);
        if(cell){
            this._cells[index] = cell;
            this._content.addChild(cell);
            let listCell = cell.getComponent(cc.Component);
            if (listCell&& listCell.setIndex) {
                listCell.setIndex(index);
            }
            let offset = this.offsetFromIndex(index)
            let delta = this._cellPositions[index + 1] - this._cellPositions[index] - this.space;
            if (this.direction == listDir.VERTICAL) {
                cell.setPosition(0, offset.y - this._content.getContentSize().height + delta * 0.5);
            } 
            else {
                cell.setPosition(offset.x + delta * 0.5, 0);
            }
        }
    }
}
